package com.wenbing.miio.mi.device.simulator;

import com.alibaba.fastjson.JSONObject;
import com.wenbing.miio.core.*;
import com.wenbing.miio.jsonrpc.JsonRpcError;
import com.wenbing.miio.jsonrpc.JsonRpcInvoker;
import com.wenbing.miio.jsonrpc.JsonRpcMessage;
import com.wenbing.miio.mi.MiCodecLayer;
import com.wenbing.miio.mi.MiDeviceUtil;
import com.wenbing.miio.mi.MiFrameLayer;
import com.wenbing.miio.util.BytesUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * @author wen bing
 * @projectName: miio
 * @package: com.wenbing.miio
 * @className: Device
 * @date 2024/09/19 15:41
 **/
public class ServerDevice extends Device {

    /**
     * 服务线程
     */
    private Thread handlerThread;

    /**
     * rpc执行器
     */
    private JsonRpcInvoker invoker;

    /**
     * 本地接口
     */
    private NetworkInterface localNetworkInterface;

    public ServerDevice(HardwareConfiguration hardwareConfiguration) throws IOException {
        this(hardwareConfiguration, 54321);
    }

    public ServerDevice(HardwareConfiguration hardwareConfiguration, int port) throws IOException {
        super(hardwareConfiguration, new LocalClock(), new DatagramSocketNetworkInterface(port));
        NetworkInterface networkInterface = new ProxyNetworkInterface(this.localNetworkInterface, getNetworkInterface().getAddress());
        this.invoker = new JsonRpcInvoker();
        this.localNetworkInterface = networkInterface;
    }

    public void register(String name, Method method) {
        this.invoker.register(name, method);
    }

    public void register(Class clazz) {
        this.invoker.register(clazz);
    }

    @Override
    public void initNetworkStack(NetworkStack stack) {
        stack.addLowerLayer(new MiFrameLayer());
        stack.addLowerLayer(new MiCodecLayer());
        stack.addLowerLayer(new ServerModeAdapterLayer());
    }
    private void handle() throws IOException {
        RowFrame frame = localNetworkInterface.receiveRowFrame();
        JsonRpcMessage message = buf2Message(frame);

        try {
            Object result = invoker.invoke(message);
            message.setResult(result);
        }
        catch (NoSuchMethodException e) {
            message.setError(JsonRpcError.methodNotFound());
        }
        catch (IllegalArgumentException e) {
            message.setError(JsonRpcError.invalidParams());
        }
        catch (InvocationTargetException | IllegalAccessException e) {
            message.setError(JsonRpcError.internalError());
        }

        ByteBuffer bf = toResultBuffer(message);
        frame.setByteBuffer(bf);
        localNetworkInterface.sendRowFrame(frame);
    }

    private JsonRpcMessage buf2Message(RowFrame frame) {
        ByteBuffer bf = frame.getByteBuffer();
        byte[] payload = Arrays.copyOfRange(bf.array(), bf.arrayOffset() + bf.position(), bf.arrayOffset() + bf.limit());
        return JSONObject.parseObject(payload, JsonRpcMessage.class);
    }

    private ByteBuffer toResultBuffer(JsonRpcMessage message) {
        JsonRpcMessage response = new JsonRpcMessage();
        response.setId(message.getId());
        response.setResult(message.getResult());
        response.setError(message.getError());
        byte[] bs = JSONObject.toJSONBytes(response);
        return ByteBuffer.wrap(bs);
    }

    @Override
    public void boot() {
        super.boot();
        ((Bootable)this.getClock()).boot();

        handlerThread = new InnerThread("MiServer-handler", this::handle);
        handlerThread.start();
        LOGGER.info("{} start success", handlerThread.getName());
        LOGGER.info("MiServer boot success, register function: {}", invoker.functionNames());
    }
}
